package com.cnpc.ds.annotation;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * ClassName: DistributionLockAspect
 * Package: com.cnpc.rtd.aop
 * Description:
 *
 *  分布式锁切面
 *
 * @author chenjy
 * @date 2022/3/28 9:33
 **/
@Aspect
@Component
@Slf4j
public class DistributionLockAspect {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private Environment environment;

    @Pointcut("@annotation(com.cnpc.ds.annotation.RedisLocked)")
    public void redisLockAspect() {}

    /**
     *  环绕通知，修改响应给前台的字典代码
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("redisLockAspect() && @annotation(redisLocked)")
    public void around(ProceedingJoinPoint point, RedisLocked redisLocked) throws Throwable {
        // 解析占位符
        String lockKey = environment.resolvePlaceholders(redisLocked.lockKey());
        // 前置处理, 获取分布式锁
        if(this.getLock(redisLocked,lockKey)) {
            try {
                // 运行目标方法
                point.proceed();
            } catch (Exception e) {
                // 异常处理
                log.error(e.getMessage());
            } finally {
                // 释放锁
                this.releaseLock(redisLocked,lockKey);
            }
        }else{
            log.info("其他系统正在执行此项任务:"+lockKey);
        }
    }

    /**
     * 获取分布式锁
     * @param redisLocked
     * @return
     */
    private boolean getLock(RedisLocked redisLocked, String lockKey) {
        // 1.获取注解的参数
        String lockPrefix = redisLocked.lockPrefix();
        long timeOut = redisLocked.timeOut();
        if(StrUtil.isEmpty(lockKey) || StrUtil.isEmpty(lockPrefix)){
            throw new RuntimeException("RedisLocked未设置lockKey、lockPrefix");
        }
        // 2.redis setNx 判断是否设置成功
        Boolean isSuccess = false;
        try{
            isSuccess = redisTemplate.opsForValue().setIfAbsent(lockPrefix + ":" + lockKey,
                    System.currentTimeMillis(),timeOut, TimeUnit.MINUTES);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("redis操作发生异常了:{}",e.getMessage());
        }finally {
            RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
        }
        if(isSuccess){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 释放分布式锁
     * @param redisLocked
     */
    private void releaseLock(RedisLocked redisLocked, String lockKey) {
        String lockPrefix = redisLocked.lockPrefix();
        try{
            redisTemplate.delete(lockPrefix + ":" + lockKey);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("redis操作发生异常了:{}",e.getMessage());
        }finally {
            RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
        }
    }
}
